
package JET.geom;

import JET.physics.Mass;
import java.util.ArrayList;
import java.util.Collection;

/**
 *
 * @author 057Ry
 */
public class MassPolygon {

    private Mass[] masses;
    private int count = 0;
    private boolean valid = false;

    // local temp vectors for fast calcultions
    private Vec2d vecA = new Vec2d(),
                  vecB = new Vec2d();

    public MassPolygon() {
        masses = new Mass[3];
    }

    public MassPolygon(Collection<Mass> mCol) {
        masses = new Mass[mCol.size()];

        for(Mass m : mCol)
            add(m);
    }

    public void clear() {
        masses = new Mass[3];
        count = 0;
        valid = false;
    }

    public int getCount() {
        return count;
    }

    public boolean isValid() {
        return valid;
    }

    public Mass getMass(int index) {
        return masses[index];
    }

    public void add(Mass m) {
        if (count<masses.length) {
            masses[count++] = m;
        } else {
            Mass[] newMasses = new Mass[masses.length + 10];

            for(int i=0; i<count; i++)
                newMasses[i] = masses[i];

            newMasses[count++] = m;
            masses = newMasses;
        }

        if (count>2)    valid = true;
        else            valid = false;
    }

    public Mass remove(int index) {
        Mass ret = masses[index];

        for(int i=index; i<count-1; i++)
            masses[i] = masses[i+1];

        count--;

        if (count>2)    valid = true;
        else            valid = false;

        return ret;
    }

    public Mass remove(Mass m) {
        for(int i=0; i<count; i++)
            if (masses[i].equals(m))
                return remove(i);

        return null;
    }

    public boolean isConvex() throws RuntimeException {
        if (valid) {
            vecA.become(masses[0].getPosition());
            vecA.sub(masses[count-1].getPosition());

            vecB.become(masses[1].getPosition());
            vecB.sub(masses[0].getPosition());

            double z = Vec2d.crossZ(vecA, vecB);

            for(int i=0; i<count-2; i++) {
                vecA.become(masses[i+1].getPosition());
                vecA.sub(masses[i].getPosition());

                vecB.become(masses[i+2].getPosition());
                vecB.sub(masses[i+1].getPosition());

                if (z * Vec2d.crossZ(vecA, vecB) < 0)
                    return false;
            }

            return true;
        } else
            throw new RuntimeException("Cannot determine if not valid mass polygon is convex!");
    }

    public boolean isClockwise() throws RuntimeException {
        if (valid) {
            vecA.become(masses[0].getPosition());
            vecA.sub(masses[count-1].getPosition());

            vecB.become(masses[1].getPosition());
            vecB.sub(masses[0].getPosition());
            double z = Vec2d.crossZ(vecA, vecB);
            for(int i=0; i<count-2; i++) {
                vecA.become(masses[i+1].getPosition());
                vecA.sub(masses[i].getPosition());

                vecB.become(masses[i+2].getPosition());
                vecB.sub(masses[i+1].getPosition());
                z += Vec2d.crossZ(vecA, vecB);
            }

            return z<=0;
        } else
            throw new RuntimeException("Cannot determine if not valid polygon is clockwise!");
    }

    /**
     * Calculates area of this polygon.
     * Note that if this polygon is clockwise area value will be lower than zero!
     *
     * @return Area of this polygon.
     * @throws RuntimeException
     */
    public double getArea() throws RuntimeException {
        if (valid) {
            double area = 0;

            for(int i=0; i<count-1; i++)
                area += masses[i].getPosition().x*masses[i+1].getPosition().y -
                        masses[i+1].getPosition().x*masses[i].getPosition().y;

            return area*.5;
        } else
            throw new RuntimeException("Cannot calculate area of non valid polygon!");
    }

    /**
     * Reverts polygon orientation.
     * Firts vertex stays at the same position.
     *
     * @throws RuntimeException
     */
    public void reverse() {
        if (!valid)
            throw new RuntimeException("Cannot reverse not valid polygon!");


        for(int i=1, n=count-1; i<=count/2; i++, n--) {
            Mass tmp = masses[i];
            masses[i] = masses[n];
            masses[n] = tmp;
        }
    }

    public String toString() {
        String s = "MassPolygon[";

        for(int i=0; i<count; i++)
            s += masses[i] + (i<count-1 ? ", " : "]");

        return s;
    }

    public static void main(String[] args) {
        MassPolygon mp = new MassPolygon();

        mp.add( new Mass(0,0, 1) );
        mp.add( new Mass(1,0, 1) );
        mp.add( new Mass(.5,.5, 1) );
        mp.add( new Mass(0,1, 1) );
        
        System.out.println(mp);
        System.out.println("isConvex: "+mp.isConvex());
        System.out.println("isClockwise: "+mp.isClockwise());
        System.out.println("area: "+mp.getArea());
        mp.reverse();

        System.out.println(mp);
        System.out.println("isConvex: "+mp.isConvex());
        System.out.println("isClockwise: "+mp.isClockwise());
        System.out.println("area: "+mp.getArea());
    }
}
